﻿using System;
using System.Collections.Generic;
using System.Linq;
using BruteForce.Animation;
using BruteForce.Characters;
using BruteForce.GameObjects;
using BruteForce.NewManagers;
using BruteForce.Screens.PlayingScreen.Map;
using BruteForce.UserInterface.Abstracts;
using BruteForce.Utilities;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Bruteforce.UserInterface.UIControls;

namespace BruteForce.Screens.PlayingScreen.Panels {
    public class GamePanel : AbstractPanel {
        private const int SquaresAcross = 20;
        private const int SquaresDown = 39;
        private const int BaseOffsetX = -32;
        private const int BaseOffsetY = -64;
        private const float HeightRowDepthMod = 0.00001f;
        public static GamePanel Current;
        private static SpriteFont _pericles6;
        private static Texture2D _highlightTexture;
        private static Texture2D _selectionTexture;
        private static Texture2D _humanDefenderInTowerTexture;
        private static Texture2D _monsterDefenderInTowerTexture;
        private static Texture2D _currentTurnCharctersTexture;

        private static Texture2D _slopeMap;
        private static Texture2D _mouseMap;
        public Player HumanPlayer;
        public List<MapDrawableTransition> MapDrawableTransitions = new List<MapDrawableTransition>();
        public Player MonsterPlayer;
        protected int UpdateLoopIndex;
        private List<Character> _lastShotCharacters;
        public int lastShotPower;
        private TileMap _myMap;
        private bool _generate;
        private Resources _generatingResources;
        private CharacterType _type;
        private Character generatedChar;

        //TUTORIAL VARIABLES
        public Tutorial.Tutorial Tutorial = null;

        //EDIT VARIABLES
        private static bool EDIT_MODE = false;
        private static int tileNum = 0;
        private static int editLoc = 0;
        private static bool editPut = false;
        private static bool editRem = false;
        private static bool editAddTower = false;
        private static bool editFlipShoot = false;
        private static bool editInitialTower = false;

        private static string mapFile = null;
        private bool firstS = false;
        public bool gameOver = false;
        private Player losser;

        public GamePanel()
            : base("Game Panel") {
            Current = this;
        }

        public Vector2 WorldLocationOfCellUnderMouse {
            get {
                Vector2 hilightLoc = Camera.ScreenToWorld(new Vector2(InputManager.Instance.CurMouseX, InputManager.Instance.CurMouseY));
                Point p = _myMap.WorldToMapCell(new Point((int)hilightLoc.X, (int)hilightLoc.Y));

                Console.WriteLine("World Cell Under Mouse: " + p);

                //Vector2 hilightLoc = Camera.ScreenToWorld(new Vector2(Mouse.GetState().X, Mouse.GetState().Y));
                //Point p = myMap.WorldToMapCell(new Point((int)hilightLoc.X, (int)hilightLoc.Y));
                return WorldLocationOfMapCell(p);
            }
        }

        public Character CharactersInCellUnderMouse {
            get { return CellUnderMouse == null ? null : CellUnderMouse.CharacterInCell; }
            set { CellUnderMouse.CharacterInCell = value; }
        }

        public MapCell CellUnderMouse {
            get {
                Vector2 hilightLoc = Camera.ScreenToWorld(new Vector2(InputManager.Instance.CurMouseX, InputManager.Instance.CurMouseY));
                Point p = _myMap.WorldToMapCell(new Point((int)hilightLoc.X, (int)hilightLoc.Y));
                //todo complete the if with maximum values
                if (p.X < 0 || p.Y < 0 || p.X >= _myMap.MapWidth || p.Y >= _myMap.MapHeight)
                    return null;

                return _myMap.Rows[p.Y].Columns[p.X];
            }
        }

        public override void Update(GameTime gameTime) {
            if (!gameOver) {
                var Current = PlayerManager.Instance.CurrentPlayer.Property;

                if (_generate && generatedChar == null)
                    CreateNewCharacter();
                KeyboardState ks = Keyboard.GetState();

                if (InputManager.CurrentClickedKeys.Contains(Keys.Space)) {
                    PlayerManager.Instance.SwitchTurns();
                }

                //EDITOR
                if (InputManager.Instance.KeyWasPressed(Keys.NumPad1)) tileNum++;
                if (InputManager.Instance.KeyWasPressed(Keys.NumPad2)) tileNum--;
                if (InputManager.Instance.KeyWasPressed(Keys.NumPad7)) tileNum = (tileNum + 10) - (tileNum % 10);
                if (InputManager.Instance.KeyWasPressed(Keys.NumPad8)) tileNum = (tileNum - 10) - (tileNum % 10);
                if (InputManager.Instance.KeyWasPressed(Keys.NumPad0)) editLoc = (editLoc + 1) % 3;
                if (InputManager.Instance.KeyWasPressed(Keys.Insert)) editPut = true;
                if (InputManager.Instance.KeyWasPressed(Keys.OemPeriod)) editAddTower = true;
                if (InputManager.Instance.KeyWasPressed(Keys.OemTilde)) editFlipShoot = true;
                if (ks.IsKeyDown(Keys.Delete)) editRem = true;
                if (InputManager.Instance.KeyWasPressed(Keys.Multiply)) _myMap.save(mapFile);

                //CAMERA
                var shiftDown = ks.IsKeyDown(Keys.LeftShift) || ks.IsKeyDown(Keys.RightShift);
                var cspeed = (shiftDown ? 10 : 5);
                if (ks.IsKeyDown(Keys.Up)) Camera.Move(new Vector2(0, -cspeed));
                if (ks.IsKeyDown(Keys.Down)) Camera.Move(new Vector2(0, cspeed));
                if (ks.IsKeyDown(Keys.Left)) Camera.Move(new Vector2(-cspeed, 0));
                if (ks.IsKeyDown(Keys.Right)) Camera.Move(new Vector2(cspeed, 0));
                if (InputManager.Instance.KeyWasPressed(Keys.Tab)) {
                    SelectNextCharacter();

                }

                if (Current.CurrentCharacter != NullCharacter.Instance &&
                    IsItHisTurn(Current.CurrentCharacter.BelongsToPlayer)) {
                    if (InputManager.Instance.KeyWasPressed(Keys.Escape)) {
                        CleanAll();
                    }
                    if (InputManager.Instance.KeyWasPressed(Keys.W)) {
                        handleWalk();
                    } else if (InputManager.Instance.KeyWasPressed(Keys.S)) {
                        handleShoot();
                    }
                }

                Character character = CharactersInCellUnderMouse;

                if (InputManager.Instance.MouseClicked.IsChangedSince(UpdateLoopIndex) &&
                    InputManager.Instance.IsMouseClickedOn(Bounds)) {
                    if (_generate) {
                        if (character == null) {
                            GenerateNewCharacter();
                        }
                    } else {
                        if (CellUnderMouse.IsHighlighted() || character != null) {
                            if (character == null &&
                                PlayerManager.Instance.CurrentPlayer.Property.CurrentCharacter.CharacterState !=
                                CharacterState.PrepareToShoot && CellUnderMouse.IsHighlighted() &&
                                Current.CurrentCharacter.CharacterState != CharacterState.Walking) {
                                HandleMovementOfCharacter();
                            } else if (character != null) {
                                HandleInteractionBetweenCharacters(character);
                            }
                        }

                    }
                }

                if (PlayerManager.Instance.CurrentPlayer.IsChangedSince((UpdateLoopIndex))) {
                    UnHighlightSelected();
                }

                UpdateLoopIndex = BFGame.LoopIndex;
            } else {
                if (InputManager.CurrentClickedKeys.Any())
                {
                    ScreenManager.Instance.PopScreen();
                }
            }

            foreach (var ui in _uiControls)
                ui.Update();

            UpdateLoopIndex = BFGame.LoopIndex;

        }

        private bool handleShoot() {
            Player Current = PlayerManager.Instance.CurrentPlayer.Property;
            if (!firstS) {
                if (Current.CurrentCharacter.Type == CharacterType.Defender ||
                    Current.CurrentCharacter.CurrentResources.Power <= 0) {
                    CleanAll();
                }
                firstS = true;
            } else {
                if (Current.CurrentCharacter.Type == CharacterType.Defender ||
                    Current.CurrentCharacter.CurrentResources.Power <= 0) {
                    CleanAll();
                }
                firstS = false;
            }
            if (Current.CurrentCharacter.CharacterState != CharacterState.Walking &&
                Current.CurrentCharacter.CharacterState != CharacterState.Shooting && !_generate &&
                Current.CurrentCharacter.CurrentResources.Power > 0
                ) {
                //handles click on "s" after highlighting exploding area
                if (Current.CurrentCharacter is Bomber &&
                    Current.CurrentCharacter.CharacterState == CharacterState.PrepareToShoot) {
                    HandleInteractionBetweenCharacters(Current.CurrentCharacter);
                    return true;
                }
                Current.CurrentCharacter.CharacterState = CharacterState.PrepareToShoot;
                InputManager.Instance.ChangeCursor(CharacterState.PrepareToShoot);
                HighlightSelected();
            }
            return false;
        }

        private void handleWalk() {
            Player Current = PlayerManager.Instance.CurrentPlayer.Property;
            CleanAll();
            if (Current.CurrentCharacter.CharacterState != CharacterState.Walking &&
                Current.CurrentCharacter.CharacterState != CharacterState.Shooting &&
                !_generate & Current.CurrentCharacter.CurrentResources.Speed > 0) {
                Current.CurrentCharacter.CharacterState = CharacterState.PrepareToWalk;
                InputManager.Instance.ChangeCursor(CharacterState.PrepareToWalk);
                Console.WriteLine("showing highlight of character: " + Current.CurrentCharacter + " on cell " +
                                  CellUnderMouse);
                HighlightSelected();
            }
        }

        public void SelectNextCharacter() {

            var currentp = PlayerManager.Instance.CurrentPlayer.Property;
            var allChars = currentp.Characters;
            var selected = currentp.CurrentCharacter;

            if (allChars.Count > 0) {
                if (selected is NullCharacter) {
                    SetSelected(allChars[0]);
                    selected = allChars[0];
                } else {
                    int selectionPos = -1;
                    int i = 0;
                    foreach (var c in allChars) {

                        if (c == selected) {
                            selectionPos = i;
                            break;
                        }

                        i++;
                    }

                    selected = allChars[(i + 1) % allChars.Count];
                    SetSelected(selected);
                }

                Camera.focus(selected.PositionInWorld);
            }
        }

        private void CleanAll() {
            InputManager.Instance.Generated();
            UnHighlightSelected();
            firstS = false;
            PlayerManager.Instance.CurrentPlayer.Property.CurrentCharacter.CharacterState = CharacterState.Idle;
        }

        public void StopGeneration() {
            generatedChar = null;
            _generate = false;
            SetSelected(NullCharacter.Instance);
            InputManager.Instance.Generated();
            UnHighlightSelected();

        }

        private bool IsItHisTurn(PlayerType belongsToPlayer) {
            return belongsToPlayer == PlayerManager.Instance.CurrentPlayer.Property.PlayerType;
        }

        private void HandleInteractionBetweenCharacters(Character character) {
            var Current = PlayerManager.Instance.CurrentPlayer.Property;
            switch (Current.CurrentCharacter.CharacterState) {
                case CharacterState.Walking:
                case CharacterState.PrepareToWalk:
                case CharacterState.Shooting:
                case CharacterState.Idle:
                    SetSelected(character);
                    break;
                case CharacterState.PrepareToShoot:
                    if (CellUnderMouse.IsHighlighted() && Current.CurrentCharacter is Archer && !IsItHisTurn(character.BelongsToPlayer) && Current.CurrentCharacter.CurrentResources.Power > 0) {
                        _lastShotCharacters = new List<Character> { character };
                        lastShotPower = Current.CurrentCharacter.CurrentResources.Power;
                        ((Archer)Current.CurrentCharacter).Shoot(WorldLocationOfCellUnderMouse);
                        Current.CurrentCharacter.CurrentResources.Power = 0;
                        UnHighlightSelected();
                    } else if (Current.CurrentCharacter is Bomber) {
                        _lastShotCharacters = GetAllEnemyCharcatersUnderHighlight(Current.CurrentCharacter);
                        _lastShotCharacters.Remove(Current.CurrentCharacter);
                        lastShotPower = Current.CurrentCharacter.CurrentResources.Power;
                        ((Bomber)Current.CurrentCharacter).Explode();

                        UnHighlightSelected();
                        SetSelected(NullCharacter.Instance);
                    }
                    break;
            }
        }

        private void HandleMovementOfCharacter() {
            var Current = PlayerManager.Instance.CurrentPlayer.Property;

            Current.CurrentCharacter.CharacterState = CharacterState.Walking;
            UnHighlightSelected();
            List<Point> path =
                _myMap.FindBestWalkingPath(Current.CurrentCharacter.MapCell,
                                           CellUnderMouse, Current.CurrentCharacter.CurrentResources.Speed + 1);


            List<Vector2> convertMapCellsToWorldLocations = ConvertMapCellsToWorldLocations(path);
            Current.CurrentCharacter.MoveTo(convertMapCellsToWorldLocations);
            HighlightSelected();

            //todo - maybe to use property
            InputManager.Instance.ChangeCursor(CharacterState.Idle);
            int numberOfSteps = path.Count - 1;

            Current.CurrentCharacter.DecreseTurnSpeed(numberOfSteps);

        }




        private void CreateNewCharacter() {
            bool human = PlayerManager.Instance.CurrentPlayer.Property.PlayerType == PlayerType.Human;
            switch (_type) {

                case CharacterType.Defender:
                    generatedChar = new Defender(_generatingResources, human, this, new Vector2(-1000, -1000), null,
                                           PlayerManager.Instance.CurrentPlayer.Property.PlayerType);

                    break;
                case CharacterType.Archer:
                    generatedChar = new Archer(_generatingResources, human, this, new Vector2(-1000, -1000), null,
                                         PlayerManager.Instance.CurrentPlayer.Property.PlayerType);
                    break;
                case CharacterType.Bomber:
                    generatedChar = new Bomber(_generatingResources, human, this, new Vector2(-1000, -1000), null,
                                         PlayerManager.Instance.CurrentPlayer.Property.PlayerType);
                    break;
            }
            var Current = PlayerManager.Instance.CurrentPlayer.Property;
            Current.CurrentCharacter = generatedChar;
        }



        private void GenerateNewCharacter() {
            Vector2 charPos = WorldLocationOfCellUnderMouse;
            MapCell mapCell = CellUnderMouse;

            if (mapCell.IsHighlighted()) {
                generatedChar.PositionInWorld = charPos;
                generatedChar.MapCell = mapCell;
                PlayerManager.Instance.CurrentPlayer.Property.AddCharacter(generatedChar);
                _generate = false;
                InputManager.Instance.Generated();
                SetSelected(generatedChar);
                generatedChar = null;
                _myMap.Lowlight();
            }
        }



        private List<Character> GetAllEnemyCharcatersUnderHighlight(Character selected) {
            List<Character> highlightedCharacters = new List<Character>();
            foreach (MapCell highlightedMapCell in TileMap.HighlightBackUp) {
                //&& highlightedMapCell.CharacterInCell.BelongsToPlayer != selected.BelongsToPlayer
                if (highlightedMapCell.CharacterInCell != null)

                    highlightedCharacters.Add(highlightedMapCell.CharacterInCell);
            }
            return highlightedCharacters;
        }

        private void SetSelected(Character newChar) {
            CleanAll();
            var Current = PlayerManager.Instance.CurrentPlayer.Property;

            if (newChar == null ||
                (newChar.BelongsToPlayer != PlayerManager.Instance.CurrentPlayer.Property.PlayerType &&
                 newChar != NullCharacter.Instance)) return;
            //                if (Selected != null)
            //                Selected = newChar;
            Current.CurrentCharacter = newChar;
            //else PlayerManager.Instance.CurrentPlayer.Property.CurrentCharacter = NullCharacter.Instance;
        }

        private void HighlightSelected() {
            //            var pos = new Vector2(InputManager.Instance.CurMouseX, InputManager.Instance.CurMouseY);
            //            pos = Camera.ScreenToWorld(pos);
            var Current = PlayerManager.Instance.CurrentPlayer.Property;

            var pos = Current.CurrentCharacter.PositionInWorld;
            Point mcelp = _myMap.WorldToMapCell(pos);
            if (Current.CurrentCharacter != null) {
                switch (Current.CurrentCharacter.CharacterState) {
                    case CharacterState.PrepareToShoot:
                        if (Current.CurrentCharacter is Archer)
                            _myMap.HighlightShootRange(mcelp.X, mcelp.Y, Color.Red, Current.CurrentCharacter.CurrentResources.Light+1, Current.CurrentCharacter);
                        else
                            _myMap.HighlightBombRange(mcelp.X, mcelp.Y, Color.Red, Current.CurrentCharacter.CurrentResources.Light);
                        break;
                    case CharacterState.PrepareToWalk:
                        _myMap.HighlightWalkRange(mcelp.X, mcelp.Y, Color.Blue, Current.CurrentCharacter.CurrentResources.Speed);
                        break;
                }
            }
        }

        private void HighlightBomberSmall(Bomber c) {
            var pos = c.PositionInWorld;
            Point mcelp = _myMap.WorldToMapCell(pos);
            _myMap.HighlightBombRange(mcelp.X, mcelp.Y, Color.Red, 2);
        }


        private void UnHighlightSelected() {
            _myMap.Lowlight();
        }

        public override void Draw(GameTime gameTime, SpriteBatch spriteBatch) {

            Dictionary<MapCell, Character> characters = readCharacters();
            Vector2 mouseRealLoc = Camera.ScreenToWorld(new Vector2(InputManager.Instance.CurMouseX, InputManager.Instance.CurMouseY));
            Point mousePoint = _myMap.WorldToMapCell(new Point((int)mouseRealLoc.X, (int)mouseRealLoc.Y));

            var firstSquare = new Vector2(Camera.Location.X / Tile.TileStepX, Camera.Location.Y / Tile.TileStepY);
            var firstX = (int)firstSquare.X;
            var firstY = (int)firstSquare.Y;

            var squareOffset = new Vector2(Camera.Location.X % Tile.TileStepX, Camera.Location.Y % Tile.TileStepY);
            var offsetX = (int)squareOffset.X;
            var offsetY = (int)squareOffset.Y;

            float maxdepth = ((_myMap.MapWidth + 1) * ((_myMap.MapHeight + 1) * Tile.TileWidth)) / 10;
            float depthOffset;


            if (editRem) {
                var cell = _myMap.Rows[mousePoint.Y].Columns[mousePoint.X];
                cell.BaseTiles.Clear();
                cell.BaseTiles.Add(tileNum);
                cell.HeightTiles.Clear();
                cell.TopperTiles.Clear();
                editRem = false;
            }

            if (editAddTower) {
                _myMap.CreateAndAlocateTower(mousePoint.Y, mousePoint.X, new Resources(10, 10, 10, 10), true);
                editAddTower = false;
            }

            if (editFlipShoot) {
                var cell = _myMap.Rows[mousePoint.Y].Columns[mousePoint.X];
                cell.shootable = !cell.shootable;
                editFlipShoot = false;
            }

            if (editInitialTower) {
                var cell = _myMap.Rows[mousePoint.Y].Columns[mousePoint.X];
                cell.InitialyTowerBelongsTo = (cell.InitialyTowerBelongsTo + 1) % 3;
                editInitialTower = false;
            }

            //big for
            for (int y = 0; y < SquaresDown; y++) {
                int rowOffset = 0;
                if ((firstY + y) % 2 == 1)
                    rowOffset = Tile.OddRowXOffset;


                for (int x = 0; x < SquaresAcross; x++) {
                    int mapx = (firstX + x);
                    int mapy = (firstY + y);
                    var cell = _myMap.Rows[mapy].Columns[mapx];

                    depthOffset = 0.7f - ((mapx + (mapy * Tile.TileWidth)) / maxdepth);

                    if ((mapx >= _myMap.MapWidth) || (mapy >= _myMap.MapHeight))
                        continue;

                    //draw base tiles
                    foreach (int tileID in _myMap.Rows[mapy].Columns[mapx].BaseTiles) {
                        BFGame.SpriteBatch.Draw(
                            Tile.TileSetTexture,
                            Camera.WorldToScreen(
                                new Vector2((mapx * Tile.TileStepX) + rowOffset, mapy * Tile.TileStepY)),
                            Tile.GetSourceRectangle(tileID),
                            Color.White,
                            0.0f,
                            Vector2.Zero,
                            1.0f,
                            SpriteEffects.None,
                            1.0f);

                    }

                    //draw mouse edit mode
                    if (editLoc == 0) {
                        if (mapx == mousePoint.X && mapy == mousePoint.Y) {
                            BFGame.SpriteBatch.Draw(
                                Tile.TileSetTexture,
                                Camera.WorldToScreen(
                                    new Vector2((mapx * Tile.TileStepX) + rowOffset, mapy * Tile.TileStepY)),
                                Tile.GetSourceRectangle(tileNum),
                                Color.White,
                                0.0f,
                                Vector2.Zero,
                                1.0f,
                                SpriteEffects.None,
                                1f);

                            if (editPut) {
                                _myMap.Rows[mapy].Columns[mapx].BaseTiles.Add(tileNum);
                                editPut = false;
                            }

                        }
                    }
                }
            }

            //            DrawCharacters();

            for (int y = 0; y < SquaresDown; y++) {
                int rowOffset = 0;
                if ((firstY + y) % 2 == 1)
                    rowOffset = Tile.OddRowXOffset;


                for (int x = 0; x < SquaresAcross; x++) {
                    int mapx = (firstX + x);
                    int mapy = (firstY + y);
                    var cell = _myMap.Rows[mapy].Columns[mapx];

                    depthOffset = 0.7f - ((mapx + (mapy * Tile.TileWidth)) / maxdepth);

                    if ((mapx >= _myMap.MapWidth) || (mapy >= _myMap.MapHeight))
                        continue;

                    Character c;
                    if (characters.TryGetValue(cell, out c) && (!cell.IsTower || c.CharacterState == CharacterState.Walking)) {
                        BFGame.SpriteBatch.Draw(c.Texture,
                                                Camera.WorldToScreen(c.PositionInWorld) + c.DrawOffset +
                                                new Vector2(-4, /*charY*/ +2),
                                                c.Texture.Bounds, Color.White,
                                                0, Vector2.Zero, 0.6f, SpriteEffects.None, 0f);

                        //                    BFGame.SpriteBatch.DrawString(_pericles6, "" + c.MapCell.Pos, Camera.WorldToScreen(c.PositionInWorld) + c.DrawOffset +
                        //                                            new Vector2(-4, charY + 2),
                        //                                              Color.White);
                    }
                    int heightRow = 0;

                    //draw hight tiles
                    foreach (int tileID in _myMap.Rows[mapy].Columns[mapx].HeightTiles) {
                        Texture2D tx;
                        Rectangle rect;

                        if (tileID < 0) {
                            KeyValuePair<Texture2D, Rectangle> temp = Tile.Textures[-tileID];
                            tx = temp.Key;
                            rect = temp.Value;
                        } else {
                            tx = Tile.TileSetTexture;
                            rect = Tile.GetSourceRectangle(tileID);
                        }

                        BFGame.SpriteBatch.Draw(
                            tx,
                            Camera.WorldToScreen(
                                new Vector2(
                                    (mapx * Tile.TileStepX) + rowOffset,
                                    mapy * Tile.TileStepY - (heightRow * Tile.HeightTileOffset))),
                            rect,
                            Color.White,
                            0.0f,
                            Vector2.Zero,
                            1.0f,
                            SpriteEffects.None,
                            depthOffset - (heightRow * HeightRowDepthMod));
                        heightRow++;
                    }

                    //draw height map edit mode
                    if (editLoc == 1 && mapx == mousePoint.X && mapy == mousePoint.Y) {
                        Texture2D tx;
                        Rectangle rect;

                        tx = Tile.TileSetTexture;
                        rect = Tile.GetSourceRectangle(tileNum);

                        BFGame.SpriteBatch.Draw(
                            tx,
                            Camera.WorldToScreen(
                                new Vector2(
                                    (mapx * Tile.TileStepX) + rowOffset,
                                    mapy * Tile.TileStepY - (heightRow * Tile.HeightTileOffset))),
                            rect,
                            Color.White,
                            0.0f,
                            Vector2.Zero,
                            1.0f,
                            SpriteEffects.None,
                            depthOffset - (heightRow * HeightRowDepthMod));
                        heightRow++;

                        if (editPut) {

                            _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(tileNum);
                            if (tileNum == 226) {
                                _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(216);
                                _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(206);
                                _myMap.Rows[mapy].Columns[mapx + 1].HeightTiles.Add(227);
                                _myMap.Rows[mapy].Columns[mapx + 1].HeightTiles.Add(217);
                                _myMap.Rows[mapy].Columns[mapx + 1].HeightTiles.Add(207);
                                _myMap.Rows[mapy].Columns[mapx + 1].HeightTiles.Add(197);
                                _myMap.Rows[mapy].Columns[mapx + 1].HeightTiles.Add(187);
                                _myMap.Rows[mapy].Columns[mapx + 2].HeightTiles.Add(228);
                                _myMap.Rows[mapy].Columns[mapx + 2].HeightTiles.Add(218);
                                _myMap.Rows[mapy].Columns[mapx + 2].HeightTiles.Add(208);
                                _myMap.Rows[mapy].Columns[mapx + 2].HeightTiles.Add(198);
                                _myMap.Rows[mapy].Columns[mapx + 2].HeightTiles.Add(188);
                                _myMap.Rows[mapy].Columns[mapx + 3].HeightTiles.Add(229);
                                _myMap.Rows[mapy].Columns[mapx + 3].HeightTiles.Add(219);
                                _myMap.Rows[mapy].Columns[mapx + 3].HeightTiles.Add(209);
                            }
                            if (tileNum == 190) {
                                _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(220);
                                _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(210);
                                _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(200);
                            }
                            if (tileNum == 191) {
                                _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(221);
                                _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(211);
                                _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(201);
                            }
                            if (tileNum == 192) {
                                _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(222);
                                _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(212);
                            }
                            if (tileNum == 193) {
                                _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(223);
                                _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(213);
                            }
                            if (tileNum == 194) {
                                _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(224);
                                _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(214);
                            }
                            if (tileNum == 195) {
                                _myMap.Rows[mapy].Columns[mapx].HeightTiles.Add(205);
                            }

                            editPut = false;
                        }
                    }

                    //draw topper tiles
                    foreach (int tileID in _myMap.Rows[y + firstY].Columns[x + firstX].TopperTiles) {
                        BFGame.SpriteBatch.Draw(
                            Tile.TileSetTexture,
                            Camera.WorldToScreen(
                                new Vector2((mapx * Tile.TileStepX) + rowOffset, mapy * Tile.TileStepY)),
                            Tile.GetSourceRectangle(tileID),
                            Color.White,
                            0.0f,
                            Vector2.Zero,
                            1.0f,
                            SpriteEffects.None,
                            depthOffset - (heightRow * HeightRowDepthMod));
                    }

                    if (editLoc == 2 && mapx == mousePoint.X && mapy == mousePoint.Y) {
                        BFGame.SpriteBatch.Draw(
                            Tile.TileSetTexture,
                            Camera.WorldToScreen(
                                new Vector2((mapx * Tile.TileStepX) + rowOffset, mapy * Tile.TileStepY)),
                            Tile.GetSourceRectangle(tileNum),
                            Color.White,
                            0.0f,
                            Vector2.Zero,
                            1.0f,
                            SpriteEffects.None,
                            depthOffset - (heightRow * HeightRowDepthMod));

                        if (editPut) {
                            _myMap.Rows[mapy].Columns[mapx].TopperTiles.Add(tileNum);
                            editPut = false;
                        }
                    }

                    //update vlads row depth
                    if (_myMap.Rows[mapy].Columns[mapx].CharacterInCell != null)
                        _myMap.Rows[mapy].Columns[mapx].CharacterInCell.DrawDepth = depthOffset -
                                                                                    (float)(heightRow + 2) *
                                                                                    HeightRowDepthMod;

                    //put highlight on the cell if it need to be there
                    if (_myMap.Rows[mapy].Columns[mapx].Highlighted != null) {
                        DrawHighlightAt(new Point(mapx, mapy), _highlightTexture);
                    }


                    foreach (MapDrawableTransition t in MapDrawableTransitions) {
                        BFGame.SpriteBatch.Draw(t.Texture, Camera.WorldToScreen(t.WorldLocation), t.Texture.Bounds,
                                                Color.White, t.Rotation, new Vector2(0, 0), t.TextureScale,
                                                SpriteEffects.None, 0.0f);
                    }

                    //draw position of tiles for debug reason
                    //                    BFGame.SpriteBatch.DrawString(_pericles6, (x + firstX) + ", " + (y + firstY),
                    //                                                  new Vector2(
                    //                                                      (x*Tile.TileStepX) - offsetX + rowOffset + BaseOffsetX + 24,
                    //                                                      (y*Tile.TileStepY) - offsetY + BaseOffsetY + 48), Color.White, 0f,
                    //                                                  Vector2.Zero,
                    //                                                  1.0f, SpriteEffects.None, 0.0f);

                    DrawDebugCellData(mapy, mapx, x, y, offsetX, offsetY, rowOffset);
                }

                Draws.DrawBorder(Bounds, new Color(91, 91, 91), 2);
                if (Tutorial != null) Tutorial.Draw(spriteBatch);
            }

            if (!EDIT_MODE) {
                //draw highlight under mouse
                Vector2 hilightLoc =
                    Camera.ScreenToWorld(new Vector2(InputManager.Instance.CurMouseX,
                                                     InputManager.Instance.CurMouseY));
                Point hilightPoint = _myMap.WorldToMapCell(new Point((int)hilightLoc.X, (int)hilightLoc.Y));

                DrawHighlightAt(hilightPoint, _highlightTexture);
                //BFGame.SpriteBatch.DrawString(_pericles6, "mouse world over: " + hilightLoc, new Vector2(400, 0),
                //                              Color.White);

            } else {
               // BFGame.SpriteBatch.DrawString(_pericles6, "edit: " + editLoc, new Vector2(400, 0),
               //                               Color.White);
            }




            //draw characters - should not be all charecters - only the ones in the visible area of the map!! (FIX IT LATER)
            //            foreach (Character c in _myMap.CharactersInMap) {
            //                if (!c.MapCell.IsTower)
            //                {
            //                    int charY = -_myMap.GetOverallHeight(c.PositionInWorld);
            //
            //                    BFGame.SpriteBatch.Draw(c.Texture,
            //                                            Camera.WorldToScreen(c.PositionInWorld) + c.DrawOffset +
            //                                            new Vector2(-4, charY + 2),
            //                                            c.Texture.Bounds, Color.White,
            //                                            0, Vector2.Zero, 0.6f, SpriteEffects.None, c.DrawDepth);
            //
            //                }
            //            }

            //draw events
            DrawCharactersAuras();


            //            //draw events
            //            foreach (Character c in _myMap.CharactersInMap) {
            //                int charY = -_myMap.GetOverallHeight(c.PositionInWorld);
            //                BFGame.SpriteBatch.Draw(c.Texture,
            //                                        Camera.WorldToScreen(c.PositionInWorld) + c.DrawOffset +
            //                                        new Vector2(-4, charY + 2),
            //                                        c.Texture.Bounds, Color.White,
            //                                        0, Vector2.Zero, 0.6f, SpriteEffects.None, c.DrawDepth);

            foreach (var ui in _uiControls)
                ui.Draw(gameTime);

            if (gameOver) {
                Vector2 firstLoc = new Vector2(200, 150);
                Vector2 secondLoc = new Vector2(250, 300);
                //                Console.WriteLine("" + ((1.0 - playerSwitchTransition.Value)));
                Color firstColor = Color.Red;
                Color secondColor = Color.Black;
                //, 255f*(1f - (float) playerSwitchTransition.Value)
                BFGame.SpriteBatch.DrawString(BFGame.BigMessageFont, (losser.PlayerType == PlayerType.Monster ? "Humans" : "Monsters") + " Player Won !!!", firstLoc, firstColor);
                BFGame.SpriteBatch.DrawString(BFGame.MessageFont, "Press any key to continue...", secondLoc, secondColor);
            }
        }

        private void DrawDebugCellData(int mapy, int mapx, int x, int y, int offsetX, int offsetY, int rowOffset) {
           // var cell = _myMap.Rows[mapy].Columns[mapx];
           // BFGame.SpriteBatch.DrawString(_pericles6, (cell.InitialyTowerBelongsTo != 0 ? "" + cell.InitialyTowerBelongsTo : ""),
            //                                                  new Vector2(
             //                                                     (x * Tile.TileStepX) - offsetX + rowOffset + BaseOffsetX + 24,
              //                                                    (y * Tile.TileStepY) - offsetY + BaseOffsetY + 48), Color.Red, 0f,
               //                                               Vector2.Zero,
                //                                              1.0f, SpriteEffects.None, 0f);
        }

        private Dictionary<MapCell, Character> readCharacters() {
            Dictionary<MapCell, Character> ret = new Dictionary<MapCell, Character>();
            foreach (Character c in _myMap.CharactersInMap) {
                ret[c.MapCell] = c;
            }

            return ret;
        }


        public void DrawCharacter() {
            foreach (Character c in _myMap.CharactersInMap) {

                //draw the character
                if (!c.MapCell.IsTower || c.CharacterState == CharacterState.Walking) {
                    BFGame.SpriteBatch.Draw(c.Texture,
                                            Camera.WorldToScreen(c.PositionInWorld) + c.DrawOffset +
                                            new Vector2(-4, /*charY*/ +2),
                                            c.Texture.Bounds, Color.White,
                                            0, Vector2.Zero, 0.6f, SpriteEffects.None, c.DrawDepth);

                    //                    BFGame.SpriteBatch.DrawString(_pericles6, "" + c.MapCell.Pos, Camera.WorldToScreen(c.PositionInWorld) + c.DrawOffset +
                    //                                            new Vector2(-4, charY + 2),
                    //                                              Color.White);
                }
            }
        }

        public void DrawCharactersAuras() {
            foreach (Character c in _myMap.CharactersInMap) {
                int charY = -_myMap.GetOverallHeight(c.PositionInWorld);
                Point selectionPoint =
                    _myMap.WorldToMapCell(new Point((int)c.PositionInWorld.X, (int)c.PositionInWorld.Y));


                //draw the character
                if (!c.MapCell.IsTower || c.CharacterState == CharacterState.Walking) {
                    //                    BFGame.SpriteBatch.Draw(c.Texture,
                    //                                             Camera.WorldToScreen(c.PositionInWorld) + c.DrawOffset +
                    //                                             new Vector2(-4, /*charY*/ +2),
                    //                                             c.Texture.Bounds, Color.White,
                    //                                             0, Vector2.Zero, 0.6f, SpriteEffects.None, c.DrawDepth);

                    //                    BFGame.SpriteBatch.DrawString(_pericles6, "" + c.MapCell.Pos, Camera.WorldToScreen(c.PositionInWorld) + c.DrawOffset +
                    //                                            new Vector2(-4, charY + 2),
                    //                                              Color.White);


                    if (IsItHisTurn(c.BelongsToPlayer))
                        DrawHighlightAt(selectionPoint, _currentTurnCharctersTexture);
                } else {
                    //draw on tower the players image
                    Texture2D img;
                    img = c.BelongsToPlayer == PlayerType.Monster
                              ? UIControlManager.Instance.MonsterPlayerSmallIconTexture
                              : UIControlManager.Instance.HumanPlayerSmallIconTexture;
                    BFGame.SpriteBatch.Draw(
                        img,
                        Camera.WorldToScreen(c.PositionInWorld) + c.DrawOffset +
                        new Vector2(5, charY / 2),
                        new Rectangle(c.Texture.Bounds.X, c.Texture.Bounds.Y, c.Texture.Bounds.Width * 3 / 4,
                                      c.Texture.Bounds.Height * 3 / 4),
                        Color.White,
                        0, Vector2.Zero, 0.6f, SpriteEffects.None, c.DrawDepth);

                    if (c is Defender) {
                        //                        DrawHighlightAt(selectionPoint, _defenderInTowerTexture);

                        img = c.BelongsToPlayer == PlayerType.Monster
                                                      ? _monsterDefenderInTowerTexture
                                                      : _humanDefenderInTowerTexture;
                        BFGame.SpriteBatch.Draw(
                            img,
                            Camera.WorldToScreen(c.PositionInWorld) + c.DrawOffset +
                            new Vector2(-35, 0),
                            new Rectangle(c.Texture.Bounds.X, c.Texture.Bounds.Y, c.Texture.Bounds.Width * 3 / 4,
                                          c.Texture.Bounds.Height * 3 / 4),
                            Color.White,
                            0, Vector2.Zero, 0.6f, SpriteEffects.None, c.DrawDepth);
                    }
                }

                //draw selection rectengle
                if (c == PlayerManager.Instance.CurrentPlayer.Property.CurrentCharacter) {
                   // BFGame.SpriteBatch.DrawString(_pericles6, "position: " + selectionPoint, new Vector2(200, 0),
                   //                               Color.White);
                    DrawHighlightAt(selectionPoint, _selectionTexture);
                }
            }
        }

        public static void LoadContent(ContentManager content) {
            _highlightTexture = content.Load<Texture2D>(@"Textures\TileSets\hilight");
            _selectionTexture = content.Load<Texture2D>(@"Textures\TileSets\selection");

            _humanDefenderInTowerTexture = content.Load<Texture2D>(@"Textures\TileSets\humanDefenderInTower");
            _monsterDefenderInTowerTexture = content.Load<Texture2D>(@"Textures\TileSets\monsterDefenderInTower");


            _currentTurnCharctersTexture = content.Load<Texture2D>(@"Textures\TileSets\currentTurnCharcters");


            Tile.TileSetTexture = content.Load<Texture2D>(@"Textures\TileSets\part4_tileset");
            Tile.CastleTexture = content.Load<Texture2D>(@"Textures\Buildings\tower2");

            _pericles6 = content.Load<SpriteFont>(@"Fonts\Pericles6");

            _mouseMap = content.Load<Texture2D>(@"Textures\TileSets\mousemap");
            _slopeMap = content.Load<Texture2D>(@"Textures\TileSets\part9_slopemaps");

            Tile.CutContent();
            Character.LoadContent(content);
        }

        public void InitializeTutorial() {
            Tutorial = new Tutorial.Tutorial();
        }

        public void InitializeTileMap(int height, int width, string mapFile) {
            GamePanel.mapFile = mapFile;
            _myMap = new TileMap(_mouseMap, _slopeMap, height, width);
            if (mapFile != null) {
                _myMap.load(mapFile);
            }

            if (_myMap.Towers.Count == 0) {
                _myMap.CreateAndAlocateTower(10, 10, new Resources(10, 10, 10, 10), true);
                _myMap.CreateAndAlocateTower(5, 5, new Resources(10, 10, 10, 10), true);
            }

        }


        public override void AfterLayout() {
            var humanTower = _myMap.HumanInitialTower;
            HumanPlayer = new Player(PlayerType.Human, Color.Red,
                                     new Resources(30, 50, 40, 80),
                                     new Resources(0, 0, 0, 0),
                                     new Defender(new Resources(0, 0, 10, 10), true, Current,
                                                  WorldLocationOfMapCell(new Point((int)humanTower.Pos.X,
                                                                                   (int)humanTower.Pos.Y)),
                                                  humanTower
                                                  , PlayerType.Human));

            var monsterTower = _myMap.MonsterInitialTower;
            MonsterPlayer = new Player(PlayerType.Monster, Color.Blue,
                                       new Resources(28, 47, 30, 78),
                                       new Resources(0, 0, 0, 0),
                                       new Defender(new Resources(0, 0, 10, 10), false, Current,
                                                    WorldLocationOfMapCell(new Point((int)monsterTower.Pos.X,
                                                                                     (int)monsterTower.Pos.Y)),
                                                    monsterTower, PlayerType.Monster));
            PlayerManager.Instance.AddPlayer(HumanPlayer, PlayerType.Human);
            PlayerManager.Instance.AddPlayer(MonsterPlayer, PlayerType.Monster);

            PlayerManager.Instance.CurrentPlayer.Property = HumanPlayer;
            Camera.ViewWidth = BFGame.Graphics.PreferredBackBufferWidth;
            Camera.ViewHeight = BFGame.Graphics.PreferredBackBufferHeight;
            Camera.WorldWidth = ((_myMap.MapWidth - 2) * Tile.TileStepX);
            Camera.WorldHeight = ((_myMap.MapHeight - 2) * Tile.TileStepY);
            Camera.DisplayOffset = new Vector2(BaseOffsetX, BaseOffsetY);


            SetSelected(HumanPlayer.CurrentCharacter);
            if (Tutorial != null) Tutorial.Initialize(new Vector2(37, 47), this);
        }

        private static Vector2 WorldLocationOfMapCell(Point p) {
            int rowOffset = 0;
            if ((p.Y) % 2 == 1)
                rowOffset = Tile.OddRowXOffset;
            var ret = new Vector2((p.X * Tile.TileStepX) + rowOffset + 32, (p.Y + 2) * Tile.TileStepY + 8);
            //Console.WriteLine("World Position of Cell Is: " + ret);
            return ret;
        }


        private void DrawHighlightAt(Point mapCell, Texture2D hlt) {
            int hilightrowOffset = 0;
            if ((mapCell.Y) % 2 == 1)
                hilightrowOffset = Tile.OddRowXOffset;

            BFGame.SpriteBatch.Draw(
                hlt,
                Camera.WorldToScreen(
                    new Vector2(
                        (mapCell.X * Tile.TileStepX) + hilightrowOffset,
                        (mapCell.Y + 2) * Tile.TileStepY)),
                new Rectangle(0, 0, 64, 32),
                Color.White * 0.4f,
                0.0f,
                Vector2.Zero,
                1.0f,
                SpriteEffects.None,
                0.0f);
        }

        //todo - to prevent allocation on some map cells
        public void InsertCharacterToMap(Character c) {
            if (c.MapCell == null) {
                Console.WriteLine("map cell is null");
                return;
            }
            c.UpdateTileLocation(_myMap);
            _myMap.CharactersInMap.Add(c);
        }

        public void UpdateCharacterCellLocation(Character c) {

            MapCell FromMapCell = c.MapCell;
            if (c.MapCell == null) return;
            c.UpdateTileLocation(_myMap);

            MapCell toMapCell = c.MapCell;
            if (FromMapCell.IsTower)
                PlayerManager.Instance.GetPlayer(c.BelongsToPlayer).RemoveTower(FromMapCell);
            if (toMapCell.IsTower)
                PlayerManager.Instance.GetPlayer(c.BelongsToPlayer).AddTower(toMapCell);

        }

        private List<Vector2> ConvertMapCellsToWorldLocations(List<Point> cells) {
            return cells.Select(WorldLocationOfMapCell).ToList();
        }


        public void AfterBomberBeenHit(Bomber b, Vector2 startPos) {
            UnHighlightSelected();
            HighlightBomberSmall(b);
            List<Character> highlightedCharacters = new List<Character>();
            foreach (MapCell highlightedMapCell in TileMap.HighlightBackUp) {
                //&& highlightedMapCell.CharacterInCell.BelongsToPlayer != selected.BelongsToPlayer
                if (highlightedMapCell.CharacterInCell != null)

                    highlightedCharacters.Add(highlightedMapCell.CharacterInCell);
            }
            highlightedCharacters.Remove(b);
            _lastShotCharacters = highlightedCharacters;
            lastShotPower = b.CurrentResources.Power;
            AfterShooting(startPos);
            UnHighlightSelected();
            SetSelected(NullCharacter.Instance);
        }

        public void AfterShooting(Vector2 startPos) {
            foreach (Character character in _lastShotCharacters) {
                character.BeenShot(startPos);
                //                _lastShotCharacters.Remove(character);
            }
            InputManager.Instance.Generated();
        }

        public void RemoveCharacter(Character character) {
            _myMap.CharactersInMap.Remove(character);
            character.MapCell.CharacterInCell = null;
        }

        public void Generate(CharacterType chosenCharacter, Resources resources) {
            //            StopGeneration();
            _myMap.HighlightGenerateRange(Color.Blue);
            _generate = true;
            _type = chosenCharacter;
            _generatingResources = resources;
        }

        public void HandleShootingPressedButton() {
            handleShoot();
        }

        public void HandleWalkingPressedButton() {
            handleWalk();
        }

        public void HandleDismissPressedButton() {
            Character c = PlayerManager.Instance.CurrentPlayer.Property.CurrentCharacter;
            c.DecreaseLife(c.InitialResources.Life);
            UnHighlightSelected();
        }

        public void GameOver(Player looser) {
            gameOver = true;
            losser = looser;
        }
    }
}